/*
 * @Descripttion: 素材管理
 * @version: 1.0
 * @Author: 自由如风
 * @Date: 2021-04-13 17:53:56
 * @LastEditors: 自由如风
 * @LastEditTime: 2021-07-02 13:34:43
 */
import fs from 'fs'
import os from 'os'
import path from 'path';
import FormData from 'form-data'
import { WX } from '..'
import { create_axios } from '../tool/WXAxios'
import { Downloader } from '../tool/Downloader'


export type types = 'image' | 'voice' | 'video' | 'thumb'


/** 临时素材管理类 */
export default class Media {

    private GET = 'https://api.weixin.qq.com/cgi-bin/media/get';
    private UPLOAD = 'https://api.weixin.qq.com/cgi-bin/media/upload';

    private axios;

    constructor(public wx: WX) {
        this.axios = create_axios(wx)
    }

    /**
     * 通过URL上传文件【不稳定】
     * @param type 
     * @param url 需要上传的文件
     * @param suffix 保存的临时文件名后缀，上传素材文件的时候，如果不指定这个参数可能上传失败，报错 40005
     * @description 内部通过下
     */
    public async uploadByURL(type: types, url: string | URL, suffix?: '.jpg' | '.txt' | '.png' | '.jpeg' | string) {

        const dir = await fs.promises.mkdtemp(path.join(os.tmpdir(), 'weixin-nodejs-'))

        const r = Math.random()

        const t = Date.now();

        const _path = path.join(dir, `${t}-${r}${suffix}`)

        console.log(_path);

        const downloader = new Downloader(url);

        await downloader.save(_path)

        return this.upload(type, fs.createReadStream(_path));

    }

    /**
     * 
     * @param media 只读文件流
     * @param type 媒体文件类型，分别有图片（image）、语音（voice）、视频（video）和缩略图（thumb）
     * @returns 返回微信服务器响应结果
     * @description 这个方法适用与上传本地文件
     */
    public async upload(type: types, media: fs.ReadStream): Promise<{
        /** 媒体文件类型，分别有图片（image）、语音（voice）、视频（video）和缩略图（thumb，主要用于视频与音乐格式的缩略图） */
        type: types,
        /** 媒体文件上传后，获取标识 */
        media_id: string,
        /** 缩略图文件标识 */
        thumb_media_id: string,
        /** 媒体文件上传时间戳 */
        created_at: string,
    }> {
        const form = new FormData();
        form.append('media', media)

        return new Promise((resolve, reject) => {
            form.getLength(async (err, length) => {
                this.axios.post(this.UPLOAD, form, {
                    params: { type },
                    headers: { ...form.getHeaders(), 'Content-Length': length }
                }).then((data: any) => {
                    resolve(data)
                }).catch(e => {
                    reject(e)
                })
            })
        })
    }


    /**
     * 上传图片(upload函数的简写)
     * @param media 要上传的只读文件流
     * @returns 
     */
    public async uploadImage(media: fs.ReadStream) {
        return this.upload('image', media);
    }

    /**
     * 获取一个media_id指向的文件下载器，后续可以通过这个下载器操作这个文件。
     * @param media_id 媒体文件ID
     * @returns 下载器
     * @description 获取临时素材的下载器,此方法适用于图片、语音、缩略图，不适用于 视频
     */
    public getDownloader(media_id: string): Downloader {
        const url = new URL(this.GET);
        url.searchParams.append('media_id', media_id);
        url.searchParams.append('access_token', this.wx.access_token);
        return new Downloader(url);
    }

    /**
     * 获取视频文件的下载器，后续可以通过这个下载器操作这个视频文件。
     * @param media_id 视频媒体文件ID
     * @returns 
     */
    public async getDownloaderOfVideo(media_id: string): Promise<Downloader> {
        const { video_url } = await this.getVideo(media_id)
        const url = new URL(video_url);
        url.searchParams.append('access_token', this.wx.access_token);
        return new Downloader(url);
    }

    /**
     * 请求下载视频媒体文件接口
     * @param media_id 视频媒体文件ID
     * @returns 
     */
    public async getVideo(media_id: string): Promise<{
        /** 如果返回的是视频消息素材，则内容如下 */
        video_url: string
    }> {
        return this.axios.get(this.GET, { params: { media_id }, responseType: 'json' })
    }
}
